<template>
	<el-container>
		<el-aside width="82%">
			<el-container>
				<el-main class="nopadding">
					<div class="box">
						<canvas ref="canvasRef" class="canvas"></canvas>
					</div>
				</el-main>
			</el-container>
		</el-aside>
		<el-container>
			<el-main class="nopadding">
				<div style="margin:4px 8px">
					<div>
						<sc-select-filter
							:data="procTypeFilterData"
							:label-width="52"
							@on-change="procTypeFilterChange"></sc-select-filter>
					</div>
					<div>
						<el-input
							ref="inputRef"
							v-model.trim="reqParams.sn"
							clearable
							prefix-icon="el-icon-search"
							placeholder="输入 SN 按回车键查询"
							@keyup.enter="search" />
					</div>
				</div>
				<div style="margin:4px 8px">
					<el-card>
						<template #header>
							<div class="card-header">
								<span>已处理编号</span>
							</div>
						</template>
						<el-space direction="vertical">
							<div v-for="(v, i) in detectSet" :key="i" class="text item">
								<span>{{ i + 1 }}</span>
								<span style="margin-left: 20px;">{{ v }}</span>
							</div>
						</el-space>
						<template #footer>
							<el-button
								round
								:loading="isSaveing"
								:disabled="!detectSet.size"
								type="primary"
								size="large"
								@click="submit">
								提交
							</el-button>
						</template>
					</el-card>
				</div>
			</el-main>
		</el-container>

		<!-- 右键菜单（绝对定位，且默认是隐藏的） -->
		<div ref="menuRef" class="menu-x" v-show="showMenu">
			<div v-for="v in menuItems" :key="v" class="menu-li" @click="submit2(v)">{{ v }}</div>
		</div>
	</el-container>
</template>

<script setup lang="ts">
import { ref, reactive, watch, getCurrentInstance, onMounted } from 'vue';
import { fabric } from 'fabric';

const instance = getCurrentInstance();
const proxy2 = {
	proxy: instance?.proxy,
	globalProp: instance!.appContext.config.globalProperties,
};

fabric.Object.prototype.objectCaching = true;

const inputRef = ref();
const isSaveing = ref(false);
const reqParams = ref({
	sn: '' // 查询 SN
});

const canvasRef: any = ref(null);
const menuRef: any = ref(null);
const showMenu: any = ref(false);
const menuPoint = ref({x: 0, y: 0});

const procTypeFilterData = ref();
const menuItems = ref([]);

const detectSet = reactive(new Set<string>()); // 缺陷处理集合

const currentProcType = ref(''); // 当前工艺
const currentFormula = ref(''); // 当前配方
let canvas: fabric.Canvas = null;
let canPointClick: boolean = false; // 是否运行锚点点击事件
let detectConfig: any = {}; // 缺陷配置项
let canvasTemplate: any = {}; // canvas 模板信息

onMounted(async () => {
    canvas = new fabric.Canvas(canvasRef.value, {
        isDrawingMode: false, // 开启自由绘画模式
        freeDrawingBrush: new fabric.PencilBrush({ decimate: 8 }),
        // backgroundColor : '#FFFFFF',
        fireRightClick: true, // 启用右键，button的数字为3
        stopContextMenu: true, // 禁止默认右键菜单
    });

    // canvas 鼠标按下事件
    canvas.on('mouse:down', e => {
        canvasMousedownHandler(e);
    });
    canvas.on('object:moving', e => {
        checkBoudningBox(e);
    });
    canvas.on('object:rotating', e => {
        checkBoudningBox(e);
    });
    canvas.on('object:scaling', e => {
        checkBoudningBox(e);
    });

    const resp0 = await proxy2.globalProp.$API.canvas.config.get();
    if (resp0.code !== 200) {
        proxy2.globalProp.$message.warning('没有找到 canvas 配置');
        return;
    }

    detectConfig = resp0.data;
    currentProcType.value = detectConfig.options[0].code;
    menuItems.value = detectConfig.options[0].menus;
    const procTypeOptions = detectConfig.options.map(s => ({ label: s.name, value: s.code }));
    procTypeFilterData.value = [{title: "工艺", key: "classify", options: [...procTypeOptions]}];

    await setCanvas(currentProcType.value);

    // 设置锚点
    if (canvasTemplate) {
      setCanvasOptions(canvasTemplate.points);
    }
 });

 watch(currentProcType, async (newValue) => {
    const selectedOptions = detectConfig.options.find(s => s.code === newValue);
    menuItems.value = selectedOptions.menus;
    await setCanvas(currentProcType.value);
    if (canvasTemplate) {
      setCanvasOptions(canvasTemplate.points);
    }
 });

  function mousedownHandler(e) {
    if (!canPointClick) {
        return;
    }

	const rect = e.target._objects[0];

    // e.button: 1-左键；2-中键；3-右键
    // 在画布上点击：e.target 为 null
    if (e.button === 3) {
        // 将菜单展示出来
        showmenu(e);
        return;
    }

    // 第一次按下，将状态变为 "已处理" 状态；
    // 第二次按下，回滚到初始状态
    if (!detectSet.has(rect.op.name)) {
        rect.set('fill', getPointColor(3));
        rect.set('stroke', '#FF7256');
        detectSet.add(rect.op.name);
    } else {
        rect.set('fill', getPointColor(rect.op.state));
        rect.set('stroke', '#000');
        detectSet.delete(rect.op.name);
    }
  }

  function canvasMousedownHandler(e) {
    // 点击空跑处时，隐藏菜单
    if (!e.target) {
        hiddemenu();
    }
  }

  function showmenu(e) {
    const rect = e.target._objects[0];
    const rectWidth = rect.width;
    const rectheight = rect.height;

    // 获取菜单组件的宽高
    const menuWidth = menuRef.value.offsetWidth;
    const menuHeight = menuRef.value.offsetHeight;

    // 当前控件位置
    let pointX = e.target.left;
    let pointY = e.target.top;

    pointX += rectWidth;
    pointY += rectheight;

    // 计算菜单出现的位置
    // 如果鼠标靠近画布右侧，菜单就出现在鼠标指针左侧
    if (canvasRef.value.width - pointX <= menuWidth) {
        pointX -= menuWidth;
    }
    // 如果鼠标靠近画布底部，菜单就出现在鼠标指针上方
    if (canvasRef.value.height - pointY <= menuHeight) {
        pointY -= menuHeight;
    }

    menuPoint.value.x = pointX;
    menuPoint.value.y = pointY;
    showMenu.value = true;
  }

  function hiddemenu() {
    showMenu.value = false;
  }

  function procTypeFilterChange(data) {
    currentProcType.value = data.classify;
  }

  // 查询数据
  async function search() {
    if (!reqParams.value.sn) {
        return;
    }

    detectSet.clear();
    canPointClick = true;

    // 从服务端获取检测不合格的点位信息
    const dectetObj = {
        formula: 'P01',
        detects: [Math.floor(Math.random() * 6), Math.floor(Math.random() * 6)],
    };

    // 通过配方找到对应的背景图和编号锚点数据
    // 在配方有变化时才进行切换
    if (currentFormula.value !== dectetObj.formula) {
        currentFormula.value = dectetObj.formula;
        canvas.clear(); // 清除 Canvas 内容

        await setCanvas(currentProcType.value, dectetObj.formula);
    } else {
        const objs = canvas.getObjects();
        objs.forEach(obj => {
            const rect = obj._objects[0];
            rect.set('fill', getPointColor(1));
            rect.set('stroke', '#000');
        });
        canvas.renderAll();
    }

    if (!canvasTemplate) {
      return;
    }

    // 将缺陷数据与锚点模板数据结合，更新 canvas
    const canvasPoints = JSON.parse(JSON.stringify(canvasTemplate.points)); // 深度拷贝
    canvasPoints.forEach(item => {
        if (dectetObj.detects.some(s => s === item.no)) {
            item.state = 2;
        }
    });

    setCanvasOptions(canvasPoints);

    reqParams.value.sn = '';
  }

  function submit() {
    // 提示还有 NG 但未完工的编号
    // 不能反复提交
    isSaveing.value = true;

    isSaveing.value = false;

    // 成功提交后
    canPointClick = false;
    detectSet.clear();
  }

  function submit2(reason) {
    console.log('submit2', reason);
  }

  // 设置 Canvas
  async function setCanvas(procType: String = '', formula: String = '') {
      canvasTemplate = null;

      const resp = await proxy2.globalProp.$API.canvas.points.get({"procType": procType, 'formula': formula}); // 获取配方
      if (resp.code !== 200) {
          proxy2.globalProp.$message.warning('没有找到指定配方的配置数据');
          return;
      }

      canvasTemplate = resp.data;
      // 设置 canvas 尺寸
      canvas.setWidth(resp.data.width ?? 900);
      canvas.setHeight(resp.data.height ?? 600);

      // 使用fabric的api加载图片
      fabric.Image.fromURL(
          proxy2.globalProp.$API.canvas.imageUrl.get(procType, formula), // 图片服务器路径
          img => { //图片加载完成后的回调函数
              // 设置图片在画布中的位置
              //img.top = 50;
              //img.left = 50;
              canvas.setBackgroundImage(img);
              canvas.renderAll();
          },
      );
  }

  // 设置 Canvas 上的锚点
  function setCanvasOptions(canvasOptions: any, canSelectable: boolean = false) {
    canvasOptions.forEach(option => {
        const rect = new fabric.Rect({
            width: option.width,
            height: option.height,
            rx: 5, // 圆角半径（x轴方向）
            ry: 5, // 圆角半径（y轴方向）
            fill: getPointColor(option.state), // 对象的填充颜色
            strokeWidth: 1.2, // 外边框线宽度
            stroke: '#000', // 边框颜色
            op: option, // 自定义
        });

        const text = new fabric.Text(option.name, {
            fontSize: option.fontSize,
            left: 0.5 * option.width,
            top: 0.5 * option.height,
            originX: 'center',
            originY: 'center',
            centeredRotation: true,
            fill: '#000',
        });

        const group = new fabric.Group([rect, text], {
            left: option.left,
            top: option.top,
            selectable: canSelectable,
        });
        group.on('mousedown', e => {
            mousedownHandler(e);
        });

	    canvas.add(group);
    });
  }

// 此为对象坐标原点设置在中心的情况
function checkBoudningBox(e) {
  const obj = e.target;
  if (!obj) {
    return;
  }

  obj.setCoords();
  const objBoundingBox = obj.getBoundingRect();
  if (objBoundingBox.top < 0) {
    obj.set('top', objBoundingBox.height/2);
    obj.setCoords();
  }

  if (objBoundingBox.left > canvas.width - objBoundingBox.width) {
    obj.set('left', canvas.width - objBoundingBox.width/2);
    obj.setCoords();
  }

  if (objBoundingBox.top > canvas.height - objBoundingBox.height) {
    obj.set('top', canvas.height - objBoundingBox.height/2);
    obj.setCoords();
  }

  if (objBoundingBox.left < 0) {
    obj.set('left', objBoundingBox.width/2);
    obj.setCoords();
  }
}

  function getPointColor(state: number) : String {
    // canvasTemplate.colors[state];
    switch (state) {
        case 1: return '#95e1d3';
        case 2: return 'red';
        case 3: return 'yellow';
        default:
            return '';
    }
  }
</script>

<style lang="scss" scoped>
/* 容器，相对定位 */
.box {
   position: relative;
   margin-left: 8px;
}

 /* 画布，给个边框 */
 .canvas {
   border: 1px solid #ccc;
 }

/* 菜单 */
.menu-x {
   z-index: 100;
   position: absolute;
   top: v-bind("(menuPoint.y) + 'px'");
   left: v-bind("(menuPoint.x) + 'px'");;
   box-sizing: border-box;
   border-radius: 4px;
   box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
   background-color: #fff;
 }

 /* 菜单每个选项 */
 .menu-li {
   box-sizing: border-box;
   padding: 4px 8px;
   height: 38px;
   border-bottom: 1px solid #ccc;
   color: #000;
   display: flex;
   justify-content: center;
   align-items: center;
   cursor: pointer;
 }

 /* 鼠标经过的选项，更改背景色 */
 .menu-li:hover {
   background-color: antiquewhite;
 }

 /* 第一个选项，顶部两角是圆角 */
 .menu-li:first-child {
   border-top-left-radius: 4px;
   border-top-right-radius: 4px;
 }

 /* 最后一个选项，底部两角是圆角，底部不需要边框 */
 .menu-li:last-child {
   border-bottom: none;
   border-bottom-left-radius: 4px;
   border-bottom-right-radius: 4px;
 }

 .card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>
